/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.logging.rebind;

import java.io.PrintWriter;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

/**
 * Generate real OPLoggers for domains, depending on the values of all OPLogging properties in .gwt.xml file.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 05 April 2011
 */
public class OPLoggingOracleGenerator extends Generator {
    
    /**
     * package name for the oracle.
     */
    public static final String ORACLE_PACKAGE_NAME = "fr.openparts.OpenParts.logging.client";
    
    /**
     * class name of the oracle implementation.
     */
    public static final String ORACLE_IMPL_NAME = "OPLoggingOracleImpl";
    
    /**
     * class name of the oracle interface.
     */
    public static final String ORACLE_INTERFACE_NAME = "OPLoggingOracle";


    /**
     * Called by the GWT Compiler when it match the specific rules as defined in OPLogging.gwt.xml. None of the passed parameters should be null.
     * 
     * @see com.google.gwt.core.ext.Generator#generate(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.GeneratorContext, java.lang.String)
     */
    @Override
    public String generate(final TreeLogger logger, final GeneratorContext context, final String loggingName) throws UnableToCompleteException {
        assert logger != null : "parameter logger shouldn't be null";
        assert context != null : "parameter context shouldn't be null";
        assert loggingName != null : "parameter loggingName shouldn't be null";
        
        final SourceWriter sourceWriter = getSourceWriter(context, logger);
        if (sourceWriter != null) {
            final OPLoggingOracleWriter oracleWriter = new OPLoggingOracleWriter(context.getPropertyOracle(), sourceWriter, logger);
            oracleWriter.writeOracle();
        }
        
        return ORACLE_PACKAGE_NAME + "." + ORACLE_IMPL_NAME;
    }

    /**
     * Create the class factory for the generated oracle. Implements the oracle interface. Add also the necessary import.
     * 
     * @return the class factory for the generated oracle class. Cannot be null.
     */
    protected ClassSourceFileComposerFactory getClassFactory() {
        final ClassSourceFileComposerFactory classFactory = new ClassSourceFileComposerFactory(ORACLE_PACKAGE_NAME, ORACLE_IMPL_NAME);
        assert classFactory != null : "class factory cannot be null";

        classFactory.addImplementedInterface(ORACLE_INTERFACE_NAME);
        
        classFactory.addImport("java.util.HashMap");
        classFactory.addImport("java.util.Map");

        return classFactory;
    }
    
    /**
     * Create the source writer that will write the generated oracle class. Returns null if the oracle has already be generated.
     * 
     * @param context
     *            the context for the generation. Shouldn't be null.
     * @param logger
     *            the logger. Shouldn't be null.
     * @return the source writer that will write the generated oracle class. Can be null.
     * @throws UnableToCompleteException
     *             if it is impossible to create a writer for the generated oracle class
     */
    protected SourceWriter getSourceWriter(final GeneratorContext context, final TreeLogger logger) throws UnableToCompleteException {
        assert context != null : "parameter context shouldn't be null";;
        assert logger != null : "parameter logger shouldn't be null";;

        final ClassSourceFileComposerFactory classFactory = getClassFactory();
        assert classFactory != null : "class factory cannot be null";

        logger.log(TreeLogger.DEBUG, "Trying to create a printWriter for OPLoggingOracle.");
        final PrintWriter printWriter = context.tryCreate(logger, ORACLE_PACKAGE_NAME, ORACLE_IMPL_NAME);
        if (printWriter == null) {
            // the type already exist, as it doesn't change from permutation to permutation.
            logger.log(TreeLogger.DEBUG, "Unable to create a printWriter for OPLoggingOracle.");
            return null;
        }

        final SourceWriter generatedOracleSourceWriter = classFactory.createSourceWriter(context, printWriter);
        if (generatedOracleSourceWriter == null) {
            logger.log(TreeLogger.ERROR, "Unable to create a source writer for OPLoggingOracle.");
            throw new UnableToCompleteException();
        }

        return generatedOracleSourceWriter;
    }
}
